#pragma once
#include "../../../../include/singleton.h"
#include "../../../../include/net_interface.hpp"
#include <map>
#include <set>

#define UNKNOW_NAT                  0
#define FULL_CONE_NAT               1
#define RESTRICTED_CONE_NAT         2
#define PORT_RESTRICTED_CONE_NAT    3
#define SYMMETRIC_NAT               4

class ISession
{
public:
    virtual HNETHANDLE net_handle(void) = 0;
    virtual void OnEstablish(HNETHANDLE hHandle, HSESSION hSession) = 0;
    virtual void OnTerminate(HSESSION hSession) = 0;
    virtual void OnError(HNETHANDLE hHandle, HSESSION hSession, INT32 iModuleError, INT32 iSystemError) = 0;
    virtual void OnRecv(HSESSION hSession, const char* pData, const INT32 iLen) = 0;
};

class CClient :
    public ISession
{
public:
    CClient(void);
    ~CClient(void);

    void Reset(void);
    virtual void OnEstablish(HNETHANDLE hHandle, HSESSION hSession);
    virtual void OnTerminate(HSESSION hSession);
    virtual void OnError(HNETHANDLE hHandle, HSESSION hSession, INT32 iModuleError, INT32 iSystemError);
    virtual void OnRecv(HSESSION hSession, const char* pData, const INT32 iLen);
    
    void Disconnect(void);
    bool SendPkg(void* host);

    inline bool can_recv(void){return m_can_recv;}
    inline void set_can_recv(bool can_recv){m_can_recv = can_recv;}

    //UINT32 local_ip(void);
    //UINT16 local_port(void);

    //UINT32 peer_ip(void);
    //UINT16 peer_port(void);

    //inline UINT32 private_ip(void){return m_private_ip;}
    //inline void set_private_ip(UINT32 private_ip){m_private_ip = private_ip;}

    //inline UINT16 private_port(void){return m_private_port;}
    //inline void set_private_port(UINT16 private_port){m_private_port = private_port;}

    inline void set_net_handle(HNETHANDLE net_handle){m_net_handle = net_handle;}
    inline HNETHANDLE net_handle(void){return m_net_handle;}

    inline UINT32 get_nat_type(void){return m_nat_type;}
    void set_nat_type(UINT32 nat_type);

    DEFMEMBER(UINT32, m_p2p_id, p2p_id)
    DEFMEMBER(UINT32, m_public_ip, public_ip)
    DEFMEMBER(UINT32, m_private_ip, private_ip)
    DEFMEMBER(UINT16, m_public_port, public_port)
    DEFMEMBER(UINT16, m_private_port, private_port)
    DEFMEMBER(HSESSION, m_session, session)

protected:
    bool        m_can_recv;
    UINT16      m_nat_type;
    HNETHANDLE  m_net_handle;
};

extern CClient* g_current_pkg_client;

class CNATTestSession :
    public ISession
{
public:
    CNATTestSession(void);
    ~CNATTestSession(void);

    void Reset(void);
    virtual void OnEstablish(HNETHANDLE hHandle, HSESSION hSession);
    virtual void OnTerminate(HSESSION hSession);
    virtual void OnError(HNETHANDLE hHandle, HSESSION hSession, INT32 iModuleError, INT32 iSystemError);
    virtual void OnRecv(HSESSION hSession, const char* pData, const INT32 iLen);
    virtual HNETHANDLE net_handle(void){return get_net_handle();}

    void Disconnect(void);

    UINT32 local_ip(void);
    UINT16 local_port(void);

    UINT32 peer_ip(void);
    UINT16 peer_port(void);

    DEFMEMBER(HSESSION, m_session, session)
    DEFMEMBER(HNETHANDLE, m_net_handle, net_handle)
};

class CConeTestSession :
    public ISession
{
public:
    void Reset(void);
    virtual void OnEstablish(HNETHANDLE hHandle, HSESSION hSession);
    virtual void OnTerminate(HSESSION hSession);
    virtual void OnError(HNETHANDLE hHandle, HSESSION hSession, INT32 iModuleError, INT32 iSystemError);
    virtual void OnRecv(HSESSION hSession, const char* pData, const INT32 iLen);
    virtual HNETHANDLE net_handle(void){return get_net_handle();}
    void Disconnect(void);

    DEFMEMBER(HSESSION, m_session, session)
    DEFMEMBER(HNETHANDLE, m_net_handle, net_handle)
    DEFMEMBER(UINT32, m_p2p_id, p2p_id)
};

//extern CP2PSession* g_current_pkg_p2psession;
extern CNATTestSession* g_current_nat_test_session;

typedef std::map<UINT32, CClient*>    TClientMap;
typedef TClientMap::iterator          TClientMapIter;

typedef std::map<HSESSION, CClient*>    TSessionMap;
typedef TSessionMap::iterator           TSessionMapIter;

typedef std::set<CClient*>          TClientSet;
typedef TClientSet::iterator        TClientSetIter;

class CClientMgr
{
    DECLARE_SINGLETON(CClientMgr)
    bool Init(void);
    void UnInit(void);
public:
    UINT32 AddClient(CClient* client);
    void DelClient(UINT32 p2p_id);
    CClient* FindClient(UINT32 p2p_id);
protected:
    TClientMap          m_map_clients;
    TClientSet          m_set_clients;
    UINT32              m_current_p2p_id;
};

class CP2PUtil
{
public:
    static void s_2_pr(CClient& src_client, CClient& dst_cient);
    static void s_2_r(CClient& src_client, CClient& dst_cient);
    static void s_2_f(CClient& src_client, CClient& dst_cient);
    static void pr_2_r(CClient& src_client, CClient& dst_cient);
    static void pr_2_f(CClient& src_client, CClient& dst_cient);
    static void r_2_f(CClient& src_client, CClient& dst_cient);
    static void s_2_s(CClient& src_client, CClient& dst_cient);
    static void r_2_r(CClient& src_client, CClient& dst_cient);
    static void pr_2_pr(CClient& src_client, CClient& dst_cient);
    static void f_2_f(CClient& src_client, CClient& dst_cient);

protected:
private:
};